package com.sl.biz.modular.thread.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.google.common.collect.Maps;
import com.sl.biz.modular.thread.controller.response.ProcessThreadConfigVO;
import com.sl.biz.modular.thread.dao.domain.dto.ProcessThreadConfigDTO;
import com.sl.biz.modular.thread.dao.domain.entity.ProcessThreadConfigDO;
import com.sl.biz.modular.thread.dao.service.ProcessThreadConfigDaoService;
import com.sl.biz.modular.thread.service.ProcessThreadConfigBizService;
import com.sl.common.enums.CommonDsiableFlagEnum;
import com.sl.core.engine.thread.ThreadConfig;
import com.sl.core.engine.thread.TraceThreadPoolTaskExecutor;
import com.sl.core.engine.trans.ProcessInstance;
import com.sl.core.engine.trans.ProcessInstancePool;
import lombok.RequiredArgsConstructor;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.springframework.boot.web.context.WebServerApplicationContext;
import org.springframework.boot.web.embedded.undertow.UndertowServletWebServer;
import org.springframework.boot.web.server.WebServer;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class ProcessThreadConfigBizServiceImpl implements ProcessThreadConfigBizService {

    private final ProcessThreadConfigDaoService threadConfigDaoService;

    private final WebServerApplicationContext webServerApplicationContext;

    @Override
    public List<ProcessThreadConfigVO> list(ProcessThreadConfigDO processThreadConfig) {
        List<ProcessThreadConfigDTO> list = threadConfigDaoService.queryList(processThreadConfig);
        //还有设置runnerable线程
        List<ProcessThreadConfigVO> processThreadConfigDTOS = BeanUtil.copyToList(list, ProcessThreadConfigVO.class);

        for (ProcessThreadConfigVO processThreadConfigDTO : processThreadConfigDTOS) {
            String threadCode = processThreadConfigDTO.getThreadCode();
            TraceThreadPoolTaskExecutor traceThreadPoolTaskExecutor = ThreadConfig.INSTANCE.get(threadCode);
            if (ObjectUtil.isNull(traceThreadPoolTaskExecutor)) {
                continue;
            }
            int size = traceThreadPoolTaskExecutor.getQueue().size();
            processThreadConfigDTO.setWaiteQueueCount(size);
            processThreadConfigDTO.setActiveCount(traceThreadPoolTaskExecutor.getActiveCount());
            processThreadConfigDTO.setTaskCount(traceThreadPoolTaskExecutor.getTaskCount());
            processThreadConfigDTO.setCompletedTaskCount(traceThreadPoolTaskExecutor.getCompletedTaskCount());
        }

        ProcessThreadConfigVO webExecutorConfig = getWebExecutorConfig();
        if (ObjectUtil.isNotNull(webExecutorConfig)) {
            processThreadConfigDTOS.add(webExecutorConfig);
        }

        webExecutorConfig = getProcessInstancePoolConfig();

        if (ObjectUtil.isNotNull(webExecutorConfig)) {
            processThreadConfigDTOS.add(webExecutorConfig);
        }

        return processThreadConfigDTOS;
    }

    private ProcessThreadConfigVO getProcessInstancePoolConfig() {
        GenericKeyedObjectPool<ProcessInstancePool.ProcessInstancePoolKey, ProcessInstance> pool = ProcessInstancePool.INSTANCE.getPool();
        ProcessThreadConfigVO processThreadConfigVO = new ProcessThreadConfigVO();
        processThreadConfigVO.setActiveCount(pool.getNumActive());
        processThreadConfigVO.setWaiteQueueCount(pool.getNumWaiters());
        processThreadConfigVO.setTaskCount(pool.getCreatedCount());
        processThreadConfigVO.setCompletedTaskCount(pool.getBorrowedCount());
        processThreadConfigVO.setThreadCode("processInstancePool");
        processThreadConfigVO.setCorePoolSize(pool.getMinIdlePerKey());
        processThreadConfigVO.setMaximumPoolSize(pool.getMaxIdlePerKey());
//        processThreadConfigVO.setKeepAliveTime(pool.);
//        processThreadConfigVO.setTimeUnit();
        processThreadConfigVO.setQueueCapacity(Long.valueOf(pool.getMaxTotal()));
        processThreadConfigVO.setThreadName("流程实例池");
        processThreadConfigVO.setRejectPolicy("waite");
        processThreadConfigVO.setDisableFlag(0);
        processThreadConfigVO.setId("processInstancePool");
        return processThreadConfigVO;
    }

    private ProcessThreadConfigVO getWebExecutorConfig() {

        WebServer webServer = webServerApplicationContext.getWebServer();
//        webServer.get

        if (!(webServer instanceof UndertowServletWebServer undertowServletWebServer)) {
            return null;
//
        }
        Object undertow = ReflectUtil.getFieldValue(undertowServletWebServer, "undertow");
        //Objects.isNull(undertow) ? null : undertow.getWorker();
        if (ObjectUtil.isNull(undertow)) {
            return null;
        }
        Executor executor = (Executor) ReflectUtil.getFieldValue(undertow, "worker");
//        XnioWorker xnioWorker = (XnioWorker) executor;

        Object taskPool = ReflectUtil.getFieldValue(executor, "taskPool");
        Object ec = ReflectUtil.getFieldValue(taskPool, "executor");

        Object handoffExecutor = ReflectUtil.invoke(ec, "getHandoffExecutor");

        ProcessThreadConfigVO processThreadConfigVO = new ProcessThreadConfigVO();
        processThreadConfigVO.setActiveCount(ReflectUtil.invoke(taskPool, "getActiveCount"));
        processThreadConfigVO.setWaiteQueueCount(ReflectUtil.invoke(ec, "getQueueSize"));
//        Object taskSeq = ReflectUtil.getFieldValue(executor, "taskSeq");
//        processThreadConfigVO.setTaskCount(Long.valueOf(taskSeq.toString()));//2
        processThreadConfigVO.setCompletedTaskCount(ReflectUtil.invoke(ec, "getCompletedTaskCount"));
        processThreadConfigVO.setThreadCode("web-undertow");
        processThreadConfigVO.setCorePoolSize(ReflectUtil.invoke(taskPool, "getCorePoolSize"));
        processThreadConfigVO.setMaximumPoolSize(ReflectUtil.invoke(taskPool, "getMaximumPoolSize"));
        //            int keepAliveTime = xnioWorker.getOption(Options.WORKER_TASK_KEEPALIVE);
        processThreadConfigVO.setKeepAliveTime(ReflectUtil.invoke(taskPool, "getKeepAliveTime", TimeUnit.SECONDS));
        processThreadConfigVO.setTimeUnit(TimeUnit.SECONDS.toString());
        Integer maximumQueueSize = ReflectUtil.invoke(ec, "getMaximumQueueSize");
        processThreadConfigVO.setQueueCapacity(Long.valueOf(maximumQueueSize));
        processThreadConfigVO.setThreadName("web-undertow");
        processThreadConfigVO.setRejectPolicy(handoffExecutor.getClass().getSimpleName());
        processThreadConfigVO.setDisableFlag(0);
        processThreadConfigVO.setId("webThreadPool");
        return processThreadConfigVO;
    }

    @Override
    public Boolean save(ProcessThreadConfigDO threadConfigDO) {
        String id = threadConfigDO.getId();
        if (StrUtil.isNotBlank(id)) {
            threadConfigDaoService.updateById(threadConfigDO);
        } else {
            threadConfigDO.setDisableFlag(CommonDsiableFlagEnum.ENABLE_FLAG.getCode());
            threadConfigDaoService.save(threadConfigDO);
        }
        return true;
    }

    @Override
    public ProcessThreadConfigVO getById(ProcessThreadConfigDO threadConfigDO) {
        ProcessThreadConfigDO byId = threadConfigDaoService.getById(threadConfigDO.getId());
        return BeanUtil.copyProperties(byId, ProcessThreadConfigVO.class);
    }

    @Override
    public Boolean setStatusFlag(ProcessThreadConfigDO assertUserUse) {
        return threadConfigDaoService.setStatusFlag(assertUserUse);
    }

    @Override
    public Map<String, String> threadStackTrace(ProcessThreadConfigDO assertUserUse) {
        String id = assertUserUse.getId();
        String threadCode = null;
        if (StrUtil.equals(id, "webThreadPool")) {
            threadCode = "XNIO";
            Map<String, String> threadStackTraceMap = TraceThreadPoolTaskExecutor.getThreadStackTraceMap("XNIO");
            return threadStackTraceMap;
        }

        ProcessThreadConfigDO byId = threadConfigDaoService.getById(assertUserUse.getId());
        if (ObjectUtil.isNull(byId)) {
            return null;
        }
        threadCode = byId.getThreadCode();


        TraceThreadPoolTaskExecutor traceThreadPoolTaskExecutor = ThreadConfig.INSTANCE.get(threadCode);
        if (ObjectUtil.isNull(traceThreadPoolTaskExecutor)) {
            return Maps.newHashMap();
        }
        Map<String, String> threadStackTraceMap = TraceThreadPoolTaskExecutor.getThreadStackTraceMap(traceThreadPoolTaskExecutor.getThreadNamePrefix());
        return threadStackTraceMap;
    }

}
